#pragma once

#include <iostream>
#include <unistd.h>
#include <cstring>
#include <functional>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/wait.h>
#include <pthread.h>
#include <memory>

#include "Inetaddr.hpp"
#include "log.hpp"
// #include "threadpool.hpp"

namespace socket_namespace
{
    class Socket;
    using socketPtr = std::shared_ptr<Socket>;

    enum
    {
        SOCKET_ERROR = 1,
        BIND_ERROR,
        LISTEN_ERROR
    };

    using namespace log_namespace;
    const static int gbacklog = 8;

    class Socket
    {
    public:
        virtual void createSocketorDie() = 0;
        virtual void createBindorDie(uint16_t port) = 0;
        virtual void createListenorDie(int backlog = gbacklog) = 0;
        virtual socketPtr Accepter(inetAddr *clientaddr) = 0;
        virtual bool Connector(const std::string &peerip, uint16_t peerport) = 0;
        virtual int getsockfd() = 0;
        virtual void close() = 0;

        virtual ssize_t Recv(std::string *out) = 0;
        virtual ssize_t Send(const std::string &in) = 0;

        void buildListenSocket(uint16_t port)
        {
            createSocketorDie();
            createBindorDie(port);
            createListenorDie();
        }

        bool buildClientSocket(const std::string &peerip, uint16_t peerport)
        {
            createSocketorDie();
            return Connector(peerip, peerport);
        }

        void buildUDPSocket()
        {

        }
    };

    class TCPSocket : public Socket
    {
    public:
        TCPSocket()
        {
        }

        TCPSocket(int sockfd)
            : _sockfd(sockfd)
        {
        }

        void createSocketorDie() override
        {
            // 创建Socket
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(FATAL, "socket create error\n");
                exit(SOCKET_ERROR);
            }
            LOG(INFO, "socket create success, socketfd: %d\n", _sockfd);
        }

        void createBindorDie(uint16_t port) override
        {
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(port);
            local.sin_addr.s_addr = INADDR_ANY;

            // 绑定本主机的套接字和 对方主机的地址和端口号
            if (::bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                LOG(FATAL, "bind error!\n");
                exit(BIND_ERROR);
            }
            LOG(INFO, "bind success, sockfd: %d\n", _sockfd);
        }

        void createListenorDie(int backlog) override
        {
            if (::listen(_sockfd, gbacklog) < 0)
            {
                LOG(FATAL, "bind error\n");
                exit(LISTEN_ERROR);
            }
            LOG(INFO, "listen success\n");
        }

        socketPtr Accepter(inetAddr *clientaddr) override
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            // 获取新连接
            // accept 这个函数利用 _socketfd建立好的链接，
            // 创建一个能够处理任务的件，并返回这个文件的文件描述符
            int socketfd = ::accept(_sockfd, (struct sockaddr *)&client, &len);
            if (socketfd < 0)
            {
                LOG(WARNING, "accept error\n");
                return nullptr;
            }
            *clientaddr = inetAddr(client);
            LOG(INFO, "get a new link, client info: %s\n, socketfd is: %d\n", clientaddr->addrStr().c_str(), socketfd);

            return std::make_shared<TCPSocket>(socketfd); // C++14
        }

        bool Connector(const std::string &peerip, uint16_t peerport) override
        {
            // 在TCP协议中，客户端需要向服务器发起连接
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(peerport);

            ::inet_pton(AF_INET, peerip.c_str(), &server.sin_addr);
            int n = ::connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
            if (n < 0)
            {
                return false;
            }
            return true;
        }

        int getsockfd()
        {
            return _sockfd;
        }

        void close()
        {
            if (_sockfd > 0)
            {
                ::close(_sockfd);
            }
        }

        ssize_t Recv(std::string *out) override
        {
            char inbuffer[4096];
            ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                *out += inbuffer;
            }
            return n;
        }

        ssize_t Send(const std::string &in) override
        {
            return ::send(_sockfd, in.c_str(), in.size(), 0);
        }

        ~TCPSocket()
        {
        }

    private:
        int _sockfd;
    };
    // class UDPSocket : public Socket
    // {};
};  
